package com.comleader.ldmapdownload.util.bean;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName Line
 * @Description: 线
 * @Author zhanghang
 * @Date 2020/4/26
 * @Version V1.0
 **/
public class Line {

    /**
     * 端点1
     */
    public Point POINTA;


    /**
     * 端点2
     */
    public Point POINTB;


    public Line(Point pointA, Point pointB) {


        this.POINTA = pointA;
        this.POINTB = pointB;
    }

    public Line() {
    }

    /**
     * 判断当前线段是否包含给定的点</br>
     * 即给定的点是否在当前边上
     * @param point
     * @return
     */
    public boolean isContainsPoint(Point point) {


        boolean result = false;
        //判断给定点point与端点1构成线段的斜率是否和当前线段的斜率相同
        //给定点point与端点1构成线段的斜率k1
        Double k1 = null;
        if (point.getLng().equals(this.POINTA.getLng())) {
            k1 = Double.NEGATIVE_INFINITY;
        } else {
            k1 = div(sub(point.getLat(), this.POINTA.getLat()), sub(point.getLng(), this.POINTA.getLng()));
        }
        //当前线段的斜率k2
        Double k2 = null;
        if (this.POINTB.getLng().equals(this.POINTA.getLng())) {
            k2 = Double.NEGATIVE_INFINITY;
        } else {
            k2 = div(sub(this.POINTB.getLat(), this.POINTA.getLat()), sub(this.POINTB.getLng(), this.POINTA.getLng()));
        }
        if (k1 != null && k2 != null) {
            if (k1.equals(k2)) {
                //若斜率相同，继续判断给定点point的x是否在pointA.getLng()和pointB.getLng()之间,若在 则说明该点在当前边上
                if (sub(point.getLng(), this.POINTA.getLng()) * sub(point.getLat(), this.POINTB.getLat()) < 0) {
                    result = true;
                }
            }
        }
        return result;
    }


    //叉积
    double mult(Point a, Point b, Point c) {
        return (a.getLng()-c.getLng())*(b.getLat()-c.getLat())-(b.getLng()-c.getLng())*(a.getLat()-c.getLat());
    }


    /**
     * 给定线段是否与当前线段相交</br>
     * 相交返回true, 不相交返回false
     * @param line
     * @return
     */
    public boolean isIntersect(Line line) {


        Point aa = this.POINTA;
        Point bb = this.POINTB;
        Point cc = line.POINTA;
        Point dd = line.POINTB;
        if (Math.max(aa.getLng(), bb.getLng()) < Math.min(cc.getLng(), dd.getLng())) {
            return false;
        }
        if (Math.max(aa.getLat(), bb.getLat()) < Math.min(cc.getLat(), dd.getLat())) {
            return false;
        }
        if (Math.max(cc.getLng(), dd.getLng()) < Math.min(aa.getLng(), bb.getLng())) {
            return false;
        }
        if (Math.max(cc.getLat(), dd.getLat()) < Math.min(aa.getLat(), bb.getLat())) {
            return false;
        }
        if (mult(cc, bb, aa) * mult(bb, dd, aa) < 0 ) {
            return false;
        }
        if ( mult(aa, dd, cc) * mult(dd, bb, cc) < 0 ) {
            return false;
        }
        return true;
    }


    /**
     * 提供精确的加法运算。
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(double v1,double v2){
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }


    /**
     * 提供精确的减法运算。
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(double v1,double v2){
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }


    /**
     * 提供精确的乘法运算。
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(double v1,double v2){
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }


    private static final int DEF_DIV_SCALE = 10;


    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
     * 小数点以后10位，以后的数字四舍五入。
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static double div(double v1,double v2){
        return div(v1,v2,DEF_DIV_SCALE);
    }


    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     * @param v1 被除数
     * @param v2 除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(double v1,double v2,int scale){
        if(scale<0){
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * @description 获取一个面上所有的线段
     * @author zhanghang
     * @date 2020-08-11 14:08:56
	 * @param polygon
     * @return
     **/
    public static List<Line> getLines(List<Point> polygon) {
        List<Line> lineList = new ArrayList();
        for (int i = 0; i < polygon.size(); i++) {
            if (i < polygon.size() - 1) {
                // 如果不是最后一个点，与下一个点相连是面
                Point l = polygon.get(i);
                Point l2 = polygon.get(i + 1);
                Line line = new Line(l, l2);
                lineList.add(line);
            } else {
                // 如果是最后一个点，与第一个点相连成线
                Point l = polygon.get(i);
                Point l2 = polygon.get(0);
                Line line = new Line(l, l2);
                lineList.add(line);
            }
        }
        return lineList;
    }

}
